package com.account.book.service.impl;

import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNodeConfig;
import cn.hutool.core.lang.tree.TreeUtil;
import com.account.book.common.context.UserTokenContextHolder;
import com.account.book.common.exception.BasicInfoException;
import com.account.book.common.exception.BasicInfoStatusEnum;
import com.account.book.entity.BookMoney;
import com.account.book.entity.Category;
import com.account.book.enums.BookMoneyTypeEnum;
import com.account.book.enums.DataStatusEnum;
import com.account.book.mapper.BookMoneyMapper;
import com.account.book.mapper.CategoryMapper;
import com.account.book.request.CategoryQueryReqVo;
import com.account.book.request.CategoryReqVo;
import com.account.book.response.CategoryQueryResVo;
import com.account.book.response.CategoryResVo;
import com.account.book.service.BookService;
import com.account.book.service.CategoryService;
import com.account.book.util.RedisUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import ma.glasnost.orika.MapperFacade;
import org.apache.commons.lang3.ObjectUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * <p>
 * 分类表 服务实现类
 * </p>
 *
 * @author wky
 * @since 2022-04-02
 */
@Service
public class CategoryServiceImpl extends ServiceImpl<CategoryMapper, Category> implements CategoryService {

    private static final Logger logger = LoggerFactory.getLogger(CategoryServiceImpl.class);
    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private BookService bookService;

    @Autowired
    private CategoryMapper categoryMapper;

    @Autowired
    private BookMoneyMapper bookMoneyMapper;

    @Autowired
    private MapperFacade mapperFacade;

    /**
     * @param @param     categoryReqVo
     * @param uploadPath
     * @param bookId
     * @param request
     * @return 新建账本分类
     * @author liang-hw
     * @description
     * @date 2023/11/09 13:44:32
     */
    @Override
    @Transactional
    public void save(CategoryReqVo categoryReqVo, String uploadPath, String bookId, HttpServletRequest request) {
        // 获取用户id
        String userId = UserTokenContextHolder.getUserTokenVOByToken().getUserId();
        List<Category> categoryIds = categoryMapper.selectList(new QueryWrapper<Category>().lambda()
                .eq(Category::getBookId, categoryReqVo.getBookId())
                .eq(Category::getStatus, DataStatusEnum.ENABLE.getValue())
                .eq(Category::getName, categoryReqVo.getName()));
        if (categoryIds.size() != 0) {
            // 查询是否存在相同分类名称
            logger.error("相同账本存在相同名称:{}", categoryReqVo.getName());
            throw new BasicInfoException(BasicInfoStatusEnum.SYSTEM_ERROR.getCode(),
                    "分类名已经存在");
        }
        //bookService.isBookAuthor(bookId);
        Category category = new Category();
        BeanUtils.copyProperties(categoryReqVo, category);
        // 设置创建时间+创建人
        category.setCreateTime(new Date());
        category.setCreateBy(userId);
        category.setUpdateBy(userId);
        category.setWeightIcon("0");
        category.setStatus(1);
        category.setType(categoryReqVo.getType());
        this.save(category);
        //删除Redis缓存
        String incomeKey = bookId + ":" + "income";
        String disburseKey = bookId + ":" + "disburse";
        redisUtil.del(incomeKey, disburseKey);
    }

    /**
     * @param @param categoryId
     * @param bookId
     * @return
     * @author liang-hw
     * @description 移除账本分类
     * @date 2023/11/09 13:44:47
     */
    @Override
    public void remove(String categoryId, String bookId) {
        // 根据分类查询账本id，
        Category category = categoryMapper.selectOne(new QueryWrapper<Category>().lambda()
                .eq(Category::getStatus, "1")
                .eq(Category::getId, categoryId));
        if (null == category) {
            // 查询是否存在相同分类名称
            logger.error("该分类已经被删除，分类id：{}", categoryId);
            throw new BasicInfoException(BasicInfoStatusEnum.SYSTEM_ERROR.getCode(),
                    "该分类不存在");
        }
        Long count = bookMoneyMapper.selectCount(new QueryWrapper<BookMoney>().lambda()
                .eq(BookMoney::getCategoryId, categoryId)
                .eq(BookMoney::getStatus, "1")
        );
        // 该分类已经被记账（tb_book_money）选择，不能删除
        if (count.intValue() != 0) {
            logger.error("该分类已经被使用，不能删除，分类id：{}", categoryId);
            throw new BasicInfoException(BasicInfoStatusEnum.SYSTEM_ERROR.getCode(),
                    "分类已使用，不能删除");
        }

        // 检验是否拥有账本权限
       // bookService.isBookAuthor(category.getBookId());
        // 删除（禁用）
        category.setStatus(0);
        categoryMapper.updateById(category);
        //删除Redis缓存
        String incomeKey = bookId + ":" + "income";
        String disburseKey = bookId + ":" + "disburse";
        redisUtil.del(incomeKey, disburseKey);
    }

    /**
     * @param @param categoryQueryReqVo
     * @return @return {@link List }<{@link CategoryResVo }>
     * @author liang-hw
     * @description 查询分类
     * @date 2023/11/09 13:45:04
     */
    @Override
    public List<CategoryResVo> findList(CategoryQueryReqVo categoryQueryReqVo) {
        List<Category> categoryList = this.list(new QueryWrapper<Category>().lambda()
                .eq(Category::getBookId, categoryQueryReqVo.getBookId())
                .eq(Category::getStatus, "1")
                .eq(Category::getType, categoryQueryReqVo.getType()));
        List<CategoryResVo> categoryResVoList = mapperFacade.mapAsList(categoryList, CategoryResVo.class);
        return categoryResVoList;
    }

    /**
     * @param @param bookId
     * @return @return {@link CategoryQueryResVo }
     * @author liang-hw
     * @description 根据bookId查询分类
     * @date 2023/11/09 13:45:14
     */
    @Override
    public CategoryQueryResVo findBookIdList(String bookId) {
        //存放24小时
        Long storageTime = 24 * 7 * 60 * 60L;
        List<Tree<String>> incomeCategory = new ArrayList<>();
        List<Tree<String>> disburseCategory = new ArrayList<>();
        String incomeKey = bookId + ":" + "income";
        incomeCategory = (List<Tree<String>>) redisUtil.get(incomeKey);
        if (ObjectUtils.isEmpty(incomeCategory)) {
            List<Category> categoryList = this.list(new QueryWrapper<Category>().lambda()
                    .eq(Category::getBookId, bookId)
                    .eq(Category::getStatus, "1")
                    .eq(Category::getType, BookMoneyTypeEnum.INCOME.getValue().intValue())
            );
            incomeCategory = constructTreeNodes(categoryList);
            redisUtil.set(incomeKey, incomeCategory, storageTime);
        }
        //支出
        String disburseKey = bookId + ":" + "disburse";
        disburseCategory = (List<Tree<String>>) redisUtil.get(disburseKey);
        if (ObjectUtils.isEmpty(disburseCategory)) {
            List<Category> categoryList = this.list(new QueryWrapper<Category>().lambda()
                    .eq(Category::getBookId, bookId)
                    .eq(Category::getStatus, "1")
                    .eq(Category::getType, BookMoneyTypeEnum.DISBURSE.getValue().intValue())
            );
            disburseCategory = constructTreeNodes(categoryList);
            redisUtil.set(disburseKey, disburseCategory, storageTime);
        }
        return new CategoryQueryResVo(incomeCategory, disburseCategory);
    }

    /**
     * @param
     * @return @return {@link List }<{@link Tree }<{@link String }>>
     * @author liang-hw
     * @description 构建树形结构
     * @date 2023/12/02 19:24:49
     */
    private List<Tree<String>> constructTreeNodes(List<Category> categoryList) {
        //配置
        TreeNodeConfig treeNodeConfig = new TreeNodeConfig();
        // 自定义属性名 都要默认值的
        treeNodeConfig.setIdKey("categoryId");
        // 最大递归深度
        treeNodeConfig.setDeep(3);
        List<Tree<String>> treeNodes = TreeUtil.build(categoryList, "0", treeNodeConfig,
                (treeNode, tree) -> {
                    tree.setId(treeNode.getCategoryId());
                    tree.setParentId(treeNode.getParentId());
                    tree.setName(treeNode.getName());
                    // 扩展属性 ...
                    tree.putExtra("id", treeNode.getId());
                    tree.putExtra("bookId", treeNode.getBookId());
                    tree.putExtra("icon", treeNode.getIcon());
                    tree.putExtra("type", treeNode.getType());
                    tree.putExtra("weightIcon", treeNode.getWeightIcon());
                });
        for (Tree<String> tree : treeNodes) {
            if (tree.hasChild()) {
                tree.putExtra("isNotExist", true);
            }
        }
        return treeNodes;
    }
}
